package support

import (
	"net/http"
	"strconv"
	"time"
)

func TraceMiddleware(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ts := time.Now()
		ctx := GetTracerCtx(w, r)
		ctx.Logger.Infof("started trace context, [%s] %s", r.Method, r.URL)

		defer func() {
			ctx.Logger.Infof("trace ended in %.2fms", float64(time.Now().Sub(ts).Microseconds())/1000.0)
			c := recover()
			if c != nil {
				ctx.Logger.Errorf("request panicked, %v", c)
				ResFail(ctx, 500, "系统错误")
			}
		}()

		header := w.Header()
		header.Set("trace", strconv.FormatUint(ctx.TraceId, 10))
		h.ServeHTTP(w, r)
	})
}

func SessionMiddleware(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx := GetTracerCtx(w, r)
		s := GetSession(ctx)
		if s.isNew {
			s.Expire = 7 * (24 * time.Hour)
			sid := strconv.FormatUint(s.ID, 10)
			w.Header().Set(SessionHeaderKey, sid)
			cookie := &http.Cookie{
				Name:     SessionCookieKey,
				Value:    sid,
				Path:     "/",
				Domain:   "",
				Expires:  time.Now().Add(s.Expire),
				MaxAge:   int(s.Expire / time.Second),
				Secure:   false,
				HttpOnly: false,
				SameSite: http.SameSiteLaxMode,
			}
			http.SetCookie(w, cookie)
		}

		defer func() {
			err := s.Save()
			if err != nil {
				ctx.Logger.Errorf("写入session失败, %v", err)
			}
		}()

		h.ServeHTTP(w, r)
	})
}

func RoleMiddlewareFunc(roles ...string) func(http.Handler) http.Handler {
	return func(h http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			ctx := GetTracerCtx(w, r)
			s := GetSession(ctx)
			if s.Role == "" {
				http.Error(w, "未登录用户不允许访问", http.StatusForbidden)
				ctx.Logger.Infof("未登录用户不允许访问，sid=%d, ip=%s", s.ID, s.LastIp)
				return
			}

			auth := false
			for _, r := range roles {
				if r == s.Role {
					auth = true
					break
				}
			}
			if !auth {
				http.Error(w, "权限不足", http.StatusUnauthorized)
				ctx.Logger.Infof("访问权限不足，sid=%d, ip=%s", s.ID, s.LastIp)
				return
			}
			h.ServeHTTP(w, r)
		})
	}
}
